//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// C# Port port by: Lars Brubaker
//                  larsbrubaker@gmail.com
// Copyright (C) 2007
//
// Permission to copy, use, modify, sell and distribute this software 
// is granted provided this copyright notice appears in all copies. 
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
//          mcseemagg@yahoo.com
//          http://www.antigrain.com
//----------------------------------------------------------------------------
//
// class platform_support
//
//----------------------------------------------------------------------------
using System;
using System.Diagnostics;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;

using AGG.Image;
using AGG.RasterizerScanline;

namespace AGG.UI
{
    public abstract class AbstractWindowsFormBridge : Form
    {
        public GuiHalSurface.ImageFormats m_format;
        public GuiHalSurface.ImageFormats m_sys_format;
        private int[]      m_keymap = new int[256];
        private int      m_last_translated_key;
        public bool          m_WindowContentNeedsRedraw;
        public bool fullscreen;
        internal System.Diagnostics.Stopwatch m_StopWatch;
        protected GuiHalSurfaceWindowsFormsBase m_app;

        public AbstractWindowsFormBridge(GuiHalSurface app, GuiHalSurface.ImageFormats format)
        {
        }

        protected void SetUpFormsWindow(GuiHalSurface app, GuiHalSurface.ImageFormats format)
        {
            m_app = (GuiHalSurfaceWindowsFormsBase)app;
            m_format = format;
            m_sys_format = GuiHalSurface.ImageFormats.pix_format_undefined;
            m_WindowContentNeedsRedraw = true;
            m_StopWatch = new Stopwatch();

            switch (m_format)
            {
                case GuiHalSurface.ImageFormats.pix_format_bw:
                    m_sys_format = GuiHalSurface.ImageFormats.pix_format_bw;
                    break;

                case GuiHalSurface.ImageFormats.pix_format_gray8:
                    m_sys_format = GuiHalSurface.ImageFormats.pix_format_gray8;
                    break;

                case GuiHalSurface.ImageFormats.pix_format_gray16:
                    m_sys_format = GuiHalSurface.ImageFormats.pix_format_gray8;
                    break;

                case GuiHalSurface.ImageFormats.pix_format_rgb565:
                case GuiHalSurface.ImageFormats.pix_format_rgb555:
                    m_sys_format = GuiHalSurface.ImageFormats.pix_format_rgb555;
                    break;

                case GuiHalSurface.ImageFormats.pix_format_rgbAAA:
                case GuiHalSurface.ImageFormats.pix_format_bgrAAA:
                case GuiHalSurface.ImageFormats.pix_format_rgbBBA:
                case GuiHalSurface.ImageFormats.pix_format_bgrABB:
                    m_sys_format = GuiHalSurface.ImageFormats.pix_format_bgr24;
                    break;

                case GuiHalSurface.ImageFormats.pix_format_rgb24:
                    m_sys_format = GuiHalSurface.ImageFormats.pix_format_rgb24;
                    break;

                case GuiHalSurface.ImageFormats.pix_format_bgr24:
                    m_sys_format = GuiHalSurface.ImageFormats.pix_format_bgr24;
                    break;

                case GuiHalSurface.ImageFormats.pix_format_rgb48:
                case GuiHalSurface.ImageFormats.pix_format_bgr48:
                    m_sys_format = GuiHalSurface.ImageFormats.pix_format_bgr24;
                    break;

                case GuiHalSurface.ImageFormats.pix_format_bgra32:
                    m_sys_format = GuiHalSurface.ImageFormats.pix_format_bgra32;
                    break;
                case GuiHalSurface.ImageFormats.pix_format_abgr32:
                    m_sys_format = GuiHalSurface.ImageFormats.pix_format_abgr32;
                    break;
                case GuiHalSurface.ImageFormats.pix_format_argb32:
                    m_sys_format = GuiHalSurface.ImageFormats.pix_format_argb32;
                    break;
                case GuiHalSurface.ImageFormats.pix_format_rgba32:
                    m_sys_format = GuiHalSurface.ImageFormats.pix_format_rgba32;
                    break;

                case GuiHalSurface.ImageFormats.pix_format_bgra64:
                case GuiHalSurface.ImageFormats.pix_format_abgr64:
                case GuiHalSurface.ImageFormats.pix_format_argb64:
                case GuiHalSurface.ImageFormats.pix_format_rgba64:
                    m_sys_format = GuiHalSurface.ImageFormats.pix_format_bgra32;
                    break;

                case GuiHalSurface.ImageFormats.pix_format_rgba_float:
                    m_sys_format = GuiHalSurface.ImageFormats.pix_format_bgra32;
                    break;

                default:
                    throw new NotImplementedException("Please add this format");
            }

            m_StopWatch.Reset();
            m_StopWatch.Start();
        }

        protected override void OnKeyUp(System.Windows.Forms.KeyEventArgs windowsKeyEvent)
        {
            AGG.UI.KeyEventArgs aggKeyEvent = new AGG.UI.KeyEventArgs((AGG.UI.Keys)windowsKeyEvent.KeyData);
            m_app.OnKeyUp(aggKeyEvent);

            windowsKeyEvent.Handled = aggKeyEvent.Handled;
            windowsKeyEvent.SuppressKeyPress = aggKeyEvent.SuppressKeyPress;
            
            base.OnKeyUp(windowsKeyEvent);
        }

        protected override void OnKeyDown(System.Windows.Forms.KeyEventArgs windowsKeyEvent)
        {
            AGG.UI.KeyEventArgs aggKeyEvent = new AGG.UI.KeyEventArgs((AGG.UI.Keys)windowsKeyEvent.KeyData);
            m_app.OnKeyDown(aggKeyEvent);

            windowsKeyEvent.Handled = aggKeyEvent.Handled;
            windowsKeyEvent.SuppressKeyPress = aggKeyEvent.SuppressKeyPress;
            
            base.OnKeyDown(windowsKeyEvent);
        }

        protected override void OnKeyPress(System.Windows.Forms.KeyPressEventArgs windowsKeyPressEvent)
        {
            AGG.UI.KeyPressEventArgs aggKeyPressEvent = new AGG.UI.KeyPressEventArgs(windowsKeyPressEvent.KeyChar);
            m_app.OnKeyPress(aggKeyPressEvent);
            windowsKeyPressEvent.Handled = aggKeyPressEvent.Handled;

            base.OnKeyPress(windowsKeyPressEvent);
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            if (ClientSize.Width > 0 && ClientSize.Height > 0)
            {
                if (m_WindowContentNeedsRedraw)
                {
                    m_app.OnDraw(m_app.NewGraphics2D());
                    m_WindowContentNeedsRedraw = false;
                }

                display_pmap(e.Graphics);
            }
        }

        protected override void OnMouseDown(System.Windows.Forms.MouseEventArgs mouseEvent)
        {
            base.OnMouseDown(mouseEvent);

            int Y = mouseEvent.Y;
            Y = (int)m_app.height() - Y;

            AGG.UI.MouseEventArgs aggMouseEvent = new AGG.UI.MouseEventArgs((AGG.UI.MouseButtons)mouseEvent.Button, mouseEvent.Clicks, mouseEvent.X, Y, mouseEvent.Delta);

            m_app.SetChildCurrent(aggMouseEvent.X, aggMouseEvent.Y);
            m_app.OnMouseDown(aggMouseEvent);
        }

        protected override void OnMouseMove(System.Windows.Forms.MouseEventArgs mouseEvent)
        {
            base.OnMouseMove(mouseEvent);

            int Y = mouseEvent.Y;
            Y = (int)m_app.height() - Y;

            AGG.UI.MouseEventArgs aggMouseEvent = new AGG.UI.MouseEventArgs((AGG.UI.MouseButtons)mouseEvent.Button, mouseEvent.Clicks, mouseEvent.X, Y, mouseEvent.Delta);

            m_app.OnMouseMove(aggMouseEvent);
        }

        protected override void OnMouseUp(System.Windows.Forms.MouseEventArgs mouseEvent)
        {
            base.OnMouseUp(mouseEvent);

            int Y = mouseEvent.Y;
            Y = (int)m_app.height() - Y;

            AGG.UI.MouseEventArgs aggMouseEvent = new AGG.UI.MouseEventArgs((AGG.UI.MouseButtons)mouseEvent.Button, mouseEvent.Clicks, mouseEvent.X, Y, mouseEvent.Delta);

            m_app.OnMouseUp(aggMouseEvent);
        }

        protected override void OnMouseWheel(System.Windows.Forms.MouseEventArgs mouseEvent)
        {
            base.OnMouseWheel(mouseEvent);

            int Y = mouseEvent.Y;
            Y = (int)m_app.height() - Y;

            AGG.UI.MouseEventArgs aggMouseEvent = new AGG.UI.MouseEventArgs((AGG.UI.MouseButtons)mouseEvent.Button, mouseEvent.Clicks, mouseEvent.X, Y, mouseEvent.Delta);

            m_app.OnMouseWheel(aggMouseEvent);
        }

        public abstract void display_pmap(Graphics displayGraphics);

        public int translate(int keycode)
        {
            return m_last_translated_key = (keycode > 255) ? 0 : m_keymap[keycode];
        }

        protected override void OnPaintBackground(PaintEventArgs e)
        {
            // we handle this so that we control the background
        }

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);

            m_app.OnResize(ClientSize.Width, ClientSize.Height);
            m_app.Invalidate();

            m_app.OnResize(e);
        }

        protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
        {
            // Call on closing and check if we can close (a "do you want to save" might cancel the close. :).
            bool CancelClose;
            m_app.OnClosing(out CancelClose);

            if (CancelClose)
            {
                e.Cancel = true;
            }

            base.OnClosing(e);
        }

        protected override void OnClosed(EventArgs e)
        {
            m_app.OnClosed();

            base.OnClosed(e);
        }
    }

    public abstract class GuiHalSurfaceWindowsFormsBase : GuiHalSurface
    {
        protected AbstractWindowsFormBridge windowsFormsWindow;

        public GuiHalSurfaceWindowsFormsBase(ImageFormats format)
            : base(format)
        {
            initialWidth = 10;
            initialHeight = 10;

            Focus();
        }

        public override Stream OpenFileDialog(OpenFileDialogParams dialogParams)
        {
            Stream myStream = null;
            OpenFileDialog openFileDialog1 = new OpenFileDialog();

            openFileDialog1.InitialDirectory = dialogParams.InitialDirectory;
            openFileDialog1.Filter = dialogParams.FilterDescription + "|" + dialogParams.FilterExtension;
            openFileDialog1.FilterIndex = 2;
            openFileDialog1.RestoreDirectory = true;

            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    if ((myStream = openFileDialog1.OpenFile()) != null)
                    {
                        dialogParams.FileName = openFileDialog1.FileName;
                        return myStream;
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Error: Could not read file from disk. Original error: " + ex.Message);
                }
            }

            return null;
        }

        public override string Caption
        {
            get
            {
                return windowsFormsWindow.Text;
            }
            set
            {
                windowsFormsWindow.Text = value;
            }
        }

        public override void OnControlChanged() { }

        public override void Invalidate()
        {
            windowsFormsWindow.Invalidate();
            windowsFormsWindow.m_WindowContentNeedsRedraw = true;
        }

        public override void Invalidate(rect_d rectToInvalidate)
        {
            windowsFormsWindow.Invalidate(new Rectangle(
                (int)System.Math.Floor(rectToInvalidate.Left),
                (int)System.Math.Floor(Height - rectToInvalidate.Top),
                (int)System.Math.Ceiling(rectToInvalidate.Width),
                (int)System.Math.Ceiling(rectToInvalidate.Height)));
            windowsFormsWindow.m_WindowContentNeedsRedraw = true;
        }

        public override void OnClosing(out bool CancelClose)
        {
            CancelClose = false;
            // your app can implement this is you want to be able to override the close.
        }

        public override void Close() 
        {
        	windowsFormsWindow.Close();
        }

        public void update_window()
        {
            if(!windowsFormsWindow.IsDisposed)
            {
                Graphics displayGraphics = windowsFormsWindow.CreateGraphics();
                windowsFormsWindow.display_pmap(displayGraphics);
            }
        }

        public void Show()
        {
            windowsFormsWindow.Show();
        }

        public override void start_timer()
        {
            windowsFormsWindow.m_StopWatch.Reset();
            windowsFormsWindow.m_StopWatch.Start();
        }

        public override double elapsed_time()
        {
            windowsFormsWindow.m_StopWatch.Stop();
            return windowsFormsWindow.m_StopWatch.Elapsed.TotalMilliseconds;
        }

        public override void ShowSystemMessage(String msg)
        {
            System.Windows.Forms.MessageBox.Show(msg, "AggSharp message");
        }
    };
}
